<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="content-type" content="text/html" charset="utf-8"/>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>Title</title>
    <link rel="stylesheet" href="lib/font-awesome/css/font-awesome.css">
    <link rel="stylesheet" href="css/jquery-search-input.css">
    <style>
        #canvas{
            box-shadow: 0 0 10px 2px #f1f1f1;
        }
    </style>
</head>
<body>
<div>
    <button type="button" onclick="rAngle()">旋转</button>
    <canvas id="canvas" width="500" height="500"></canvas>

    <img class="test" />
</div>

<script src="lib/jquery-3.5.1.js"></script>
<script src="lib/fabric.min.js"></script>
<script>
    const imageUrl = 'https://file.sxkid.com/images/mkt/bf8c4d16435d4ea6aa1f1f2801d7327a.png';

    function clip() {
        set();

        let width = __clipRect.width;
        __clipRect.width = 0;
        __clipRect.strokeWidth = 0;
        var base64 = this.__canvas.toDataURL();
        __clipRect.width = width;
        __clipRect.strokeWidth = 1;
        $('.test').attr('src', base64)
    }
    function angle(){
        let img = this.__img, canvas = this.__canvas, clipRect = this.__clipRect;
        img.set('dirty', true);
        img.set({
            angle:  img.angle+90,
        });
        canvas.renderAll();
        canvas.rotate = canvas.rotate || 0;
        canvas.rotate +=90;
        canvas.rotate(canvas.rotate);
    }

    function rAngle(){
        let img = this.__img, canvas = this.__canvas, clipRect = this.__clipRect;

        const imageRatio = img.width / img.height;

        //定高
        const imageHeight = canvas.height;
        //等比缩放高
        const imageWidth = imageHeight / imageRatio;
        let x= (canvas.width - imageWidth) / 2, y = 0;
        const scale = canvas.height / img.width;
        img.set({
            angle:  img.angle-180,
            left: x + img.height * scale,
            top: y
        });

        img.scale(scale);//图片缩小
        canvas.renderAll();
    }
    //setTimeout(rAngle, 300)

    function set() {
        let img = this.__img, canvas = this.__canvas, clipRect = this.__clipRect;
        //当设置为“ true”时，对象的缓存将在下一次渲染调用时重新渲染。从1.7.0开始
        img.set('dirty', true);
        clipRect.set('dirty', true);

        let top =  -img.height * img.scaleY / 2; //使裁剪框位于图片(0,0)点
        let left =  -img.width * img.scaleX / 2;
        var rect = new fabric.Rect({
            top : top + (clipRect.top * clipRect.scaleX - img.top) / img.scaleY, //基础偏移 + 实际偏移
            left : left + (clipRect.left * clipRect.scaleY - img.left)/ img.scaleX,
            width : clipRect.width * clipRect.scaleX /img.scaleX,
            height : clipRect.height * clipRect.scaleY /img.scaleY,
            fill : 'transparent',
        });
        img.set({
            clipPath: rect
        });
    }

    (async function() {
        var canvas = this.__canvas = new fabric.Canvas('canvas');
        fabric.Object.prototype.transparentCorners = false;

        var img = this.__img = await getLocalImage(canvas);
        var rect = new fabric.Rect({
            top : -300,
            left : -600,
            width : img.width,
            height : 600,
            fill : 'transparent',
        });
        img.set({
            angle: 0,
            //clipPath: rect,
        });

        var clipRect = this.__clipRect = new fabric.Rect({
            top : img.top,
            left : img.left,
            width : img.scaleX * img.width ,
            height :img.scaleY * img.height ,
            stroke : 'black',
            strokeWidth : 1,
            fill : 'transparent'
        });

        canvas.on('object:scaling', function(e){
            clip();
        });
        canvas.on('object:moving', function(e){
            clip();
        });

        canvas.add(img);
        canvas.add(clipRect).setActiveObject(clipRect);
    })();


    async function init() {
        var canvas = new fabric.Canvas('canvas');
        console.log('canvas', canvas);
        var rect = new fabric.Rect({
            top : 100,
            left : 100,
            width : 60,
            height : 70,
            fill : 'transparent'
        });
        canvas.add(rect);
        let imageF = await getLocalImage(canvas);
        canvas.add(imageF);
    }

    function f() {
        fabric.Image.fromURL(imageUrl, function (imageF) {
            drawImage(imageF);
        })
    }

    function drawImage(imageF) {
        const scale = canvas.width / imageF.width;
        imageF.scale(scale);//图片缩小10倍
        //等比缩放高
        const imageHeight = imageF.height * scale;

        let x= 0, y = (canvas.height - imageHeight) / 2;
        imageF.top = y;
        imageF.angle = 0;

        canvas.add(imageF);
    }

    async function getLocalImage(canvas) {
        let image = await loadImage(imageUrl);

        const imageRatio = image.naturalWidth / image.naturalHeight;

        //定宽
        const imageWidth = canvas.width;
        //等比缩放高
        const imageHeight = imageWidth / imageRatio;
        let x= 0, y = (canvas.height - imageHeight) / 2;
        let imageF = new fabric.Image(image, {
            hasControls: false,
            selectable: false,
            left: x,
            top: y
        });
        const scale = canvas.width / image.naturalWidth;
        imageF.scale(scale);//图片缩小10倍
        return imageF;
    }

    function toUrl() {
        var url = canvas.toDataURL()
        console.log(url)
    }

    function loadImage(src) {
        return new Promise((resolve, reject) => {
            let image = new Image();
            if (src.indexOf('https://') === 0 || src.indexOf('http://') === 0)
                image.crossOrigin = '*';//anonymous

            image.src = src;
            image.onload = function () {
                resolve(image);
            }
            image.onerror = function () {
                reject(new Error('图片加载失败'));
            }
        })
    }


</script>
</body>
</html>